﻿namespace LinqToSqlQueryVisualizer
{
    using System;
    using System.Data;
    using System.Data.Linq;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Runtime.Serialization.Formatters.Binary;
    using Microsoft.VisualStudio.DebuggerVisualizers;

    public class Visualizer
    {
        /// <summary>
        /// This method is called from the general Query Visualizer.
        /// It reads the query data from the stream, sets the corresponding fields in the
        /// QueryVisualizerFrom and displays it.
        /// </summary>
        /// <param name="windowService">Used to show the visualizer dialog.</param>
        /// <param name="rawStream">The query data sent from the provider / visualizer proxy.</param>
        public static void Display(IDialogVisualizerService windowService, Stream rawStream)
        {
            var formatter = new BinaryFormatter();
            var expression = (string)formatter.Deserialize(rawStream);

            var qi = SqlQueryInfo.Deserialize(rawStream);
            var infos = qi.Queries;

            var connectionString = (string)formatter.Deserialize(rawStream);

            var form = new QueryVisualizerForm();
            form.SetTexts(expression, infos, connectionString);
            windowService.ShowDialog(form);
        }

        /// <summary>
        /// This method is called from the QueryVisualizer to copy the following data to the stream:
        /// 1. The query expression as string
        /// 2. SQL query text(s) / parameters
        /// 3. Connection string.
        /// </summary>
        /// <param name="context">The data context.</param>
        /// <param name="query">The query expression to visualize.</param>
        /// <param name="outgoingData">The stream used for marshalling to the visualizer.</param>
        public static void StreamQueryInfo(DataContext context, IQueryable query, Stream outgoingData)
        {
            var formatter = new BinaryFormatter();
            if (null == context)
            {
                formatter.Serialize(outgoingData, "None");
                formatter.Serialize(outgoingData, "No data context provided.");
                return;
            }

            formatter.Serialize(outgoingData, query.Expression.ToString());
            try
            {
                var qi = new SqlQueryInfo(GetFullQueryInfo(context, query));
                qi.Serialize(outgoingData);
            }
            catch (Exception ex)
            {
                outgoingData.Position = 0;
                formatter.Serialize(outgoingData, "None");
                formatter.Serialize(outgoingData, string.Format(CultureInfo.CurrentUICulture, "Exception while serializing the query:\r\n{0}", ex.Message));
                return;
            }

            IDbConnection conn = context.Connection;
            var connectionString = conn.ConnectionString;

            // Need to check for |DataDirectory| token in the connection string and replace with absolute path to allow
            if (connectionString.Contains("|DataDirectory|"))
            {
                connectionString = conn.ConnectionString.Replace(@"|DataDirectory|\", AppDomain.CurrentDomain.BaseDirectory);
            }

            // The Linq To Sql Query Visualizer to use the same connection string.
            formatter.Serialize(outgoingData, connectionString);
        }

        private static SqlQueryText[] GetFullQueryInfo(DataContext dataContext, IQueryable query)
        {
            var command = dataContext.GetCommand(query);

            var result = new SqlQueryText[1];
            for (var i = 0; i < 1; i++)
            {
                result[i].Text = command.CommandText;
                var parameterCount = command.Parameters.Count;
                result[i].Params = new ParameterText[parameterCount];
                for (var j = 0; j < parameterCount; j++)
                {
                    var parameterText = new ParameterText();
                    var parameter = command.Parameters[j];
                    parameterText.Name = parameter.ParameterName;
                    parameterText.SqlType = parameter.DbType.ToString();
                    var paramValue = parameter.Value;
                    parameterText.Value = paramValue == null ? null : parameter.Value.ToString();
                    result[i].Params[j] = parameterText;
                }
            }

            return result;
        }
    }
}